#ifndef RAY_TRACE_PRO_MESH_GEOMETRY
#define RAY_TRACE_PRO_MESH_GEOMETRY

#include "Basic/GxLibBasic.h"
#include "Geometry.h"
#include "KdTree.h"

using namespace GxLibBasic;

namespace RayTracePro
{
	struct MeshFace
	{
		float PlaneU, PlaneV, PlaneD;
		short MaterialId;
		short ProjectionAxis;
		float K_beta_u, K_beta_v, K_beta_d, K_alpha_u;
		float K_gamma_u, K_gamma_v, K_gamma_d, K_alpha_v;
		union
		{
			struct
			{
				int Normal1, Normal2, Normal3;
			};
			int Normals[3];
		};
		union
		{
			struct
			{
				int TexCoord1, TexCoord2, TexCoord3;
			};
			int TexCoords[3];
		};
	};

	class Material;

	class MeshGeometry : public Geometry
	{
	private:
		Array<BBox, true> faceBBoxes;
		KdTree<MeshFace> tree;
		static bool MeshGeometry_ComputeIntersectionPred(RayTraceSystem * system, const Geometry * geom, const Ray & ray);
		static bool MeshGeometry_ComputeIntersection(RayTraceSystem * system, DifferentialGeometry & inter, const Geometry * geom, 
			const Ray & ray, const RayDifferential & rayDiffIn, float & t);
		static void MeshGeometry_Compute_dNdx(DifferentialGeometry & inter, const Vec3 & dPdx, const Vec3 & dPdy);
	public:
		BBox Bounds;
		Array<Vec3, true> Normals;
		Array<Vec2, true> TexCoords;
		Array<MeshFace, true> Faces;
		Array<RefPtr<RayTracePro::Material>> Materials;
		inline void ReserveFaceBuffer(int size)
		{
			faceBBoxes.SetCapacity(size);
			Faces.SetCapacity(size);
		}
		void AddFace(const Vec3 verts[3], int norm[3], int tex[3], short materialId=-1);
		void UpdateAccelerateStructure();
		virtual void ComputeBBox(BBox & box);
		virtual void ComputeBoundingSpheres(int depth, Array<Vec4, true> & buffer);
		KdTree<MeshFace> * GetKdTree() 
		{
			return &tree;
		}

		MeshGeometry();
	};

	typedef bool (*GeometryLoaderFunction)(RayTraceSystem * system, MeshGeometry & geometry, const char * fileName);

	class RayTraceSystem;

	class GeometryLoader
	{
	private:
		static RefPtr<Mapping<String, GeometryLoaderFunction>> loaders;
	public:
		static bool LoadGeometryFromFile(RayTraceSystem * system, MeshGeometry & geometry, const String & fileName);
		static void Init();
		static void Finalize()
		{
			loaders = 0;
		}
	};
	bool LoadSponge(MeshGeometry & geometry, int level);
}

#endif